(ns image
  (:use image
        [clojure.java.io :only [file]]
        [util.contrib.java-utils :only [as-str]])
  (:require clojure.string)
  (:import [java.awt.image BufferedImage]
           [java.awt Color Font RenderingHints GradientPaint]
           [javax.imageio ImageIO]))

 ;;; io

(defn- guess-image-format [filename]
  (let [mapping {"PNG" :png
                 "JPEG" :jpeg
                 "JPG" :jpeg}
        ext (.toUpperCase (clojure.string/replace filename #"^.*\." ""))
        format (get mapping ext)]
    (when-not format
      (throw (Exception. (str "Cannot guess the image type of " filename))))
    format))

(defn- to-format [format]
  (let [mapping {"PNG" "PNG"}
        formatName (mapping (.toUpperCase (as-str format)))]
    (when-not formatName
      (throw (Exception. (str "Unknown format: " format))))
    formatName))

(defn- anti-alias [g]
  ; http://mindprod.com/jgloss/antialiasing.html
  (.setRenderingHint g RenderingHints/KEY_ANTIALIASING      RenderingHints/VALUE_ANTIALIAS_ON )
  (.setRenderingHint g RenderingHints/KEY_TEXT_ANTIALIASING RenderingHints/VALUE_TEXT_ANTIALIAS_ON )
  (.setRenderingHint g RenderingHints/KEY_RENDERING         RenderingHints/VALUE_RENDER_QUALITY )
  ; or:
  ;(.addRenderingHints g (RenderingHints. RenderingHints/KEY_ANTIALIASING RenderingHints/VALUE_ANTIALIAS_ON ))
  ;(.setRenderingHint g RenderingHints/KEY_TEXT_ANTIALIASING RenderingHints/VALUE_TEXT_ANTIALIAS_ON )
  ; this is not working:
  ;(System/setProperty "awt.useSystemAAFontSettings" "gasp") ; not working!
  g)

(defn new-image [w h]
  (let [image (BufferedImage. w h (BufferedImage/TYPE_INT_ARGB))
        g (.getGraphics image)]
    (anti-alias g)
    {:image image
     :g g
     :width w
     :height h}))

(defn write-image
  ([{:keys [image]} path format]
    (ImageIO/write image (to-format format) (file path)))
  ([image path]
    (write-image image path (guess-image-format path))))

;;; color

(defn- to-color [color]
  (condp = color
    :white Color/WHITE
    :black Color/BLACK
    :blue  Color/BLUE
    color))

(defn rgb
  ([c]
    (rgb c c c))
  ([r g b]
    (Color. r g b))
  ([r g b a]
    (Color. r g b a)))

(defn- set-color [g color]
  (.setColor g (to-color color)))

(defn fill-color [{:keys [g width height]} color]
  (set-color g color)
  (.fillRect g 0 0 width height))

(defn- dict-to-gradient [{:keys [top center]} w h]
  (println (to-color top) 0 (quot h 2) (to-color center) true)
  (java.awt.GradientPaint. 0 0 (to-color top) 0 (quot h 2) (to-color center) true))

(defn- to-gradient [gradient w h]
  (condp = (class gradient)
    GradientPaint gradient
    clojure.lang.PersistentArrayMap (dict-to-gradient gradient w h)))

(defn fill-gradient [{:keys [g width height]} gradient]
  (.setPaint g (to-gradient gradient width height))
  (.fillRect g 0 0 width height))

;;; text


(defn- to-font-style [style]
  (condp = style
    :plain Font/PLAIN
    :bold  Font/BOLD
    style))

(def default-image-font-size 12)

(defn make-font
  ([name style size]
    (Font. name (to-font-style style) size))
  ([name-or-seq]
    (cond
      (string? name-or-seq) (make-font name-or-seq :plain default-image-font-size)
      (sequential? name-or-seq) (apply make-font name-or-seq)
      (instance? java.awt.Font name-or-seq) name-or-seq
      :else (throw (Exception. (str "Don't know how to create font from " name-or-seq)))))
  ([name style-or-size]
    (if (number? style-or-size)
      (make-font name :plain style-or-size)
      (make-font name style-or-size default-image-font-size))))

(defn- set-font [g font]
  (.setFont g (make-font font)))

(defn draw-text [{:keys [g width height] :as i} text font size & {:keys [color style bg gradient]}]
  (let [font (make-font font (or style :plain) size)]
    (when (and bg (not= bg :none))
      (fill-color i bg))
    (when gradient
      (fill-gradient i gradient))
    (set-font g font)
    (set-color g (or color :black))
    (.drawString g text 1 (quot (+ height size) 2))))

